﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;

namespace BaseClasses
{
    /// <summary>
    /// Contains all information about graph vertexes and links between them.
    /// This graph assumes that there are at least two vertexes connected with one link.
    /// </summary>
    public class Graph : ICollection<Link>
    {
        /// <summary>
        /// 
        /// </summary>
        public ICollection<Link> Links { get { return this.links; } }
        ICollection<Link> links = new List<Link>();

        /// <summary>
        /// 
        /// </summary>
        public ICollection<Vertex> Vertexes { get { return this.vertexes; } }
        ICollection<Vertex> vertexes = new List<Vertex>();

        public Graph() { }

        #region ========== IEnumerable members ==========

        public IEnumerator<Link> GetEnumerator()
        {
            return this.links.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.links.GetEnumerator();
        }

        #endregion

        #region ========== ICollection members ==========

        #region Implemented

        public void Add(Link item)
        {
            if (item == null)
                throw new ArgumentNullException("Link cannot be null");

            // Check is not already added and if so, continue adding
            if (!links.Contains(item))
            {
                // Add link
                this.links.Add(item);

                // Check both links vertexes are present in vertexes list.
                // Alfa vertex
                if (!Vertex.ContainsVertex(this.vertexes, item.Alfa))
                    this.vertexes.Add(item.Alfa);
                // Omega vertex
                if (!Vertex.ContainsVertex(this.vertexes, item.Omega))
                    this.vertexes.Add(item.Omega);
            }
        }

        /// <summary>
        /// Gets links count.
        /// </summary>
        public int Count { get { return this.links.Count; } }

        public void Clear()
        {
            this.links.Clear();
            this.vertexes.Clear();
        }

        public bool Contains(Link item)
        {
            return this.links.Contains(item);
        }

        public bool IsReadOnly
        {
            get { return this.vertexes.IsReadOnly || this.links.IsReadOnly; }
        }

        public bool Remove(Link item)
        {
            if (!this.links.Contains(item))
                return false;
            else
            {
                this.links.Remove(item);

                // Clean up vertexes list from unused vertexes
                Vertex[] linkVertexes = { item.Alfa, item.Omega };
                foreach (Vertex vertex in linkVertexes as IEnumerable)
                    if (this.vertexes.Contains(vertex))
                        if (!IsVertexConnected(vertex))
                            this.vertexes.Remove(vertex);

                return true; // Link has been removed
            }
        }

        #endregion

        #region Not implemented

        public void CopyTo(Link[] array, int arrayIndex)
        {
            throw new NotImplementedException();
        }

        public bool IsSynchronized
        {
            get { throw new NotImplementedException(); }
        }

        public object SyncRoot
        {
            get { throw new NotImplementedException(); }
        }

        public void CopyTo(Array array, int index)
        {
            throw new NotImplementedException();
        }
        #endregion

        #endregion

        /// <summary>
        /// Checks if there is any link between given vertex and any another.
        /// </summary>
        /// <param name="vertex"></param>
        /// <returns></returns>
        bool IsVertexConnected(Vertex vertex)
        {
            foreach (Link link in this.links)
                if (link.Alfa.Equals(vertex) || link.Omega.Equals(vertex))
                    return true;

            return true;
        }

        /// <summary>
        /// Gets all links connected with given vertex.
        /// </summary>
        /// <param name="vertex">Vertex</param>
        /// <param name="graph">Graph</param>
        /// <returns>Collection of links</returns>
        public ICollection<Link> GetVertexesLinks(Vertex vertex)
        {
            ICollection<Link> links = new List<Link>();
            foreach (Link link in this)
                if (link.Alfa.Equals(vertex) || link.Omega.Equals(vertex))
                    links.Add(link);
            return links;
        }
    }
}
